/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package rukopisyimport.sourceData;

import cz.mzk.rehan.commons.Dom4jUtils;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.XPath;

/**
 *
 * @author Martin Rehanek (rehan@mzk.cz)
 */
public class BarCodeResolver {

    private static final Logger logger = Logger.getLogger(BarCodeResolver.class.getName());
    private final XPath mappingXpath = DocumentHelper.createXPath("/mappings/mapping");
    private final XPath baseXpath = DocumentHelper.createXPath("base");
    private final XPath sysnoXpath = DocumentHelper.createXPath("sysno");
    private final XPath barcodeXpath = DocumentHelper.createXPath("barcode");
    private final Map<BaseSysnoId, String> barcodeMap = new HashMap<BaseSysnoId, String>();

    public BarCodeResolver(File mapping) throws IOException, DocumentException, ParseException {
        Document mappingDoc = Dom4jUtils.loadDocument(mapping, false);
        parseMappings(mappingDoc);
    }

    /**
     *
     * @param base
     * @param sysno
     * @return barcode
     */
    public String findBarcode(String base, String sysno) throws Exception {
        BaseSysnoId baseSysno = new BaseSysnoId(base, sysno);
        String barcode = barcodeMap.get(baseSysno);
        if (barcode == null) {
            throw new Exception("No barcode found for " + baseSysno);
        }
        return barcode;
    }

    private void parseMappings(Document mappingDoc) throws ParseException {
        List<Element> mappings = mappingXpath.selectNodes(mappingDoc);
        if (mappings.isEmpty()) {
            throw new ParseException("mapping elements not found", 0);
        }
        for (Element mapping : mappings) {
            parseMapping(mapping);
        }
    }

    private void parseMapping(Element mapping) throws ParseException {
        Element baseEl = (Element) baseXpath.selectSingleNode(mapping);
        String base = elementText(baseEl);
        Element sysnoEl = (Element) sysnoXpath.selectSingleNode(mapping);
        String sysno = elementText(sysnoEl);
        Element barcodeEl = (Element) barcodeXpath.selectSingleNode(mapping);
        String barcode = elementText(barcodeEl);
        updateMappings(base, sysno, barcode);
    }

    private String elementText(Element baseEl) throws ParseException {
        if (baseEl == null) {
            throw new ParseException("element not found", 0);
        } else {
            return baseEl.getTextTrim();
        }
    }

    private void updateMappings(String base, String sysno, String barcode) {
        BaseSysnoId baseSysno = new BaseSysnoId(base, sysno);
        String barcodeFound = barcodeMap.get(baseSysno);
        if (barcodeFound != null) {
            logger.log(Level.WARNING, "multiple barcodes found for {0}: {1} and {2}", new Object[]{baseSysno, barcodeFound, barcode});
        }
        barcodeMap.put(baseSysno, barcode);
    }

    private class BaseSysnoId {

        private final String base;
        private final String sysno;

        public BaseSysnoId(String base, String sysno) {
            this.base = base;
            this.sysno = sysno;
        }

        /**
         * @return the base
         */
        public String getBase() {
            return base;
        }

        /**
         * @return the sysno
         */
        public String getSysno() {
            return sysno;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final BaseSysnoId other = (BaseSysnoId) obj;
            if ((this.base == null) ? (other.base != null) : !this.base.equals(other.base)) {
                return false;
            }
            if ((this.sysno == null) ? (other.sysno != null) : !this.sysno.equals(other.sysno)) {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 5;
            hash = 79 * hash + (this.base != null ? this.base.hashCode() : 0);
            hash = 79 * hash + (this.sysno != null ? this.sysno.hashCode() : 0);
            return hash;
        }

        @Override
        public String toString() {
            return "BaseSysnoId{" + "base=" + base + "sysno=" + sysno + '}';
        }
    }
}
